﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		VoidAsyncReult.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 //   File:            VoidAsyncReult.cs
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Threading;

namespace Dexter.Async
{
	/// <summary>
	/// 	Implementation of <c>IAsyncResult</c>
	/// </summary>
	public class VoidAsyncResult : IAsyncResult
	{
		// Fields set at construction which never change while 
		// operation is pending
		// Fields set at construction which do change after 
		// operation completes
		private const Int32 C_STATE_PENDING = 0;
		private const Int32 C_STATE_COMPLETED_SYNCHRONOUSLY = 1;
		private const Int32 C_STATE_COMPLETED_ASYNCHRONOUSLY = 2;
		private readonly AsyncCallback mAsyncCallback;
		private readonly Object mAsyncState;
		
		// Field that may or may not get set depending on usage
		private ManualResetEvent mAsyncWaitHandle;
		private Int32 mCompletedState = C_STATE_PENDING;

		// Fields set when operation completes
		private Exception mException;

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "VoidAsyncResult" /> class.
		/// </summary>
		/// <param name = "asyncCallback">The async callback.</param>
		/// <param name = "state">The state.</param>
		public VoidAsyncResult ( AsyncCallback asyncCallback , Object state )
		{
			mAsyncCallback = asyncCallback;
			mAsyncState = state;
		}

		/// <summary>
		/// 	Sets as completed.
		/// </summary>
		/// <param name = "exception">The exception.</param>
		/// <param name = "completedSynchronously">if set to <c>true</c> [completed synchronously].</param>
		public void SetAsCompleted (
			Exception exception , Boolean completedSynchronously )
		{
			// Passing null for exception means no error occurred. 
			// This is the common case
			mException = exception;

			// The m_CompletedState field MUST be set prior calling the callback
			Int32 prevState = Interlocked.Exchange ( ref mCompletedState ,
			                                         completedSynchronously
			                                         	? C_STATE_COMPLETED_SYNCHRONOUSLY
			                                         	: C_STATE_COMPLETED_ASYNCHRONOUSLY );
			if ( prevState != C_STATE_PENDING )
				throw new InvalidOperationException (
					"You can set a result only once" );

			// If the event exists, set it
			if ( mAsyncWaitHandle != null )
				mAsyncWaitHandle.Set ();

			// If a callback method was set, call it
			if ( mAsyncCallback != null )
				mAsyncCallback ( this );
		}

		/// <summary>
		/// 	This method is called when the async procedure ended.
		/// </summary>
		public void EndInvoke ()
		{
			// This method assumes that only 1 thread calls EndInvoke 
			// for this object
			if ( !IsCompleted )
			{
				// If the operation isn't done, wait for it
				AsyncWaitHandle.WaitOne ();
				AsyncWaitHandle.Close ();
				mAsyncWaitHandle = null; // Allow early GC
			}

			// Operation is done: if an exception occured, throw it
			if ( mException != null )
				throw mException;
		}

		#region Implementation of IAsyncResult

		/// <summary>
		/// 	Gets a user-defined object that qualifies or contains information about an asynchronous operation.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// 	A user-defined object that qualifies or contains information about an asynchronous operation.
		/// </returns>
		public Object AsyncState
		{
			get { return mAsyncState; }
		}

		/// <summary>
		/// 	Gets a value that indicates whether the asynchronous operation completed synchronously.
		/// </summary>
		/// <value></value>
		/// <returns>true if the asynchronous operation completed synchronously; otherwise, false.
		/// </returns>
		public Boolean CompletedSynchronously
		{
			get
			{
				return Thread.VolatileRead ( ref mCompletedState ) ==
				       C_STATE_COMPLETED_SYNCHRONOUSLY;
			}
		}

		/// <summary>
		/// 	Gets a <see cref = "T:System.Threading.WaitHandle" /> that is used to wait for an asynchronous operation to complete.
		/// </summary>
		/// <value></value>
		/// <returns>
		/// 	A <see cref = "T:System.Threading.WaitHandle" /> that is used to wait for an asynchronous operation to complete.
		/// </returns>
		public WaitHandle AsyncWaitHandle
		{
			get
			{
				if ( mAsyncWaitHandle == null )
				{
					Boolean done = IsCompleted;
					ManualResetEvent mre = new ManualResetEvent ( done );
					if ( Interlocked.CompareExchange ( ref mAsyncWaitHandle , mre , null ) != null )
					{
						// Another thread created this object's event; dispose 
						// the event we just created
						mre.Close ();
					}
					else
					{
						if ( !done && IsCompleted )
						{
							// If the operation wasn't done when we created 
							// the event but now it is done, set the event
							mAsyncWaitHandle.Set ();
						}
					}
				}
				return mAsyncWaitHandle;
			}
		}

		/// <summary>
		/// 	Gets a value that indicates whether the asynchronous operation has completed.
		/// </summary>
		/// <value></value>
		/// <returns>true if the operation is complete; otherwise, false.
		/// </returns>
		public Boolean IsCompleted
		{
			get
			{
				return Thread.VolatileRead ( ref mCompletedState ) !=
				       C_STATE_PENDING;
			}
		}

		#endregion
	}
}
